1 module hip.windowing.platforms.windows;
2 import hip.windowing.input;
3 import hip.windowing.events;
4 
5 
6 version(UWP){}
7 else version(Windows)
8     version = WindowsNative;
9 
10 version(WindowsNative)
11 {
12     import core.sys.windows.winuser;
13     import core.sys.windows.wingdi;
14     import core.sys.windows.winbase : GetModuleHandle,
15                                     GetLastError,
16                                     FormatMessage,
17                                     FORMAT_MESSAGE_ALLOCATE_BUFFER,
18                                     FORMAT_MESSAGE_FROM_SYSTEM,
19                                     LocalFree;
20     import core.sys.windows.windef;
21 
22     alias HWND = void*;
23     alias HINSTANCE = void*;
24     package const(wchar)* winClassName = "HipremeEngine";
25     package __gshared HDC hdc;
26     package HGLRC glContext;
27 
28     pragma(lib, "opengl32");
29     pragma(lib, "gdi32");
30     pragma(lib, "user32"); //Can't import that to UWP
31     pragma(lib, "kernel32");//Can't import that to UWP
32     nothrow ushort LOWORD(ulong l) {return cast(ushort) l;}
33     nothrow ushort HIWORD(ulong l) {return cast(ushort) (l >>> 16);}
34     nothrow @system int GET_X_LPARAM(LPARAM lp){return cast(int)cast(short)LOWORD(lp);}
35     nothrow @system int GET_Y_LPARAM(LPARAM lp){return cast(int)cast(short)HIWORD(lp);}
36     nothrow @system uint GET_XBUTTON_WPARAM(WPARAM wp){ return cast(uint)HIWORD(wp);}
37 
38 
39     package extern(Windows) LRESULT WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) nothrow @system
40     {
41         switch(msg)
42         {
43             case WM_CLOSE:
44                 if(onWindowClosed != null)
45                     onWindowClosed();
46                 DestroyWindow(hwnd);
47                 break;
48             case WM_DESTROY:
49                 PostQuitMessage(0);
50                 ReleaseDC(hwnd, hdc);
51                 break;
52             case WM_CHAR:
53             case WM_SYSCHAR:
54                 if(onTextInput != null)
55                     onTextInput(cast(wchar)wParam);
56                 break;
57             case WM_SYSKEYDOWN:
58             case WM_KEYDOWN:
59                 if(onKeyDown != null)
60                     onKeyDown(cast(uint)wParam);
61                 break;
62             case WM_SYSKEYUP:
63             case WM_KEYUP:
64                 if(onKeyUp != null)
65                     onKeyUp(cast(uint)wParam);
66                 break;
67             case WM_SIZE: //Resize
68             {
69                 UINT width = LOWORD(lParam);
70                 UINT height = HIWORD(lParam);
71                 if(onWindowResize != null)
72                     onWindowResize(width, height);
73                 break;
74             }
75             case WM_MOVE:
76             {
77                 break;
78             }
79             case WM_MOUSEMOVE:
80             {
81                 if(onMouseMove != null)
82                     onMouseMove(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
83                 break;
84             }
85             case WM_LBUTTONDOWN:
86                 if(onMouseDown != null)
87                     onMouseDown(HipWindowingMouseButton.left, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
88                 break;
89             case WM_MBUTTONDOWN:
90                 if(onMouseDown != null)
91                     onMouseDown(HipWindowingMouseButton.middle, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
92                 break;
93             case WM_RBUTTONDOWN:
94                 if(onMouseDown != null)
95                     onMouseDown(HipWindowingMouseButton.right, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
96                 break;
97             case WM_XBUTTONDOWN:
98                 if(onMouseDown != null)
99                     onMouseDown(
100                         GET_XBUTTON_WPARAM(wParam) == XBUTTON1 ? HipWindowingMouseButton.button1 : HipWindowingMouseButton.button2,
101                         GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)
102                     );
103                 break;
104             case WM_LBUTTONUP:
105                 if(onMouseUp != null)
106                     onMouseUp(HipWindowingMouseButton.left, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
107                 break;
108             case WM_MBUTTONUP:
109                 if(onMouseUp != null)
110                     onMouseUp(HipWindowingMouseButton.middle, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
111                 break;
112             case WM_RBUTTONUP:
113                 if(onMouseUp != null)
114                     onMouseUp(HipWindowingMouseButton.right, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
115                 break;
116             case WM_XBUTTONUP:
117                 if(onMouseUp != null)
118                     onMouseUp(
119                         GET_XBUTTON_WPARAM(wParam) == XBUTTON1 ? HipWindowingMouseButton.button1 : HipWindowingMouseButton.button2,
120                         GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)
121                     );
122                 break;
123             case WM_MOUSEWHEEL:
124                 if(onMouseWheel != null)
125                     onMouseWheel(
126                         0, cast(int)GET_WHEEL_DELTA_WPARAM(wParam)/WHEEL_DELTA
127                     );
128                 break;
129             default:
130                 return DefWindowProc(hwnd, msg, wParam, lParam);
131         }
132         return 0;
133     }
134     @nogc:
135 
136     extern(Windows) nothrow @nogc HGLRC wglCreateContextAttribs(HDC, DWORD, HWND);
137     alias wglChoosePixelFormatARBProc = extern(Windows) nothrow @nogc BOOL function(
138         HDC hdc, const(int)* piAttribFList, const float* pfAttribIList, uint nMaxFormats,
139         int* piFormats, uint* nNumFormats);
140 
141     alias wglCreateContextAttribsARBProc = extern(Windows) nothrow @nogc HGLRC function(
142         HDC hdc, HGLRC hShareContext,const int* attribList
143     );
144 
145     alias wglSwapIntervalEXTProc =  extern(Windows) nothrow @nogc int function(int interval);
146 
147 
148     wglSwapIntervalEXTProc wglSwapIntervalEXT;
149     wglChoosePixelFormatARBProc wglChoosePixelFormatARB;
150     wglCreateContextAttribsARBProc wglCreateContextAttribsARB;
151     extern(Windows) nothrow @nogc void* wglGetProcAddress(const(char)* funcName);
152 
153 
154     extern(Windows) nothrow @nogc bool initializeOpenGL(int majorVersion, int minorVersion, ref void* hwnd)
155     {
156         PIXELFORMATDESCRIPTOR pfd =
157         {
158             PIXELFORMATDESCRIPTOR.sizeof,
159             1,
160             PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER ,    // Flags
161             PFD_TYPE_RGBA,        // The kind of framebuffer. RGBA or palette.
162             32,                   // Colordepth of the framebuffer.
163             0, 0, 0, 0, 0, 0,
164             0,
165             0,
166             0,
167             0, 0, 0, 0,
168             24,                   // Number of bits for the depthbuffer
169             8,                    // Number of bits for the stencilbuffer
170             0,                    // Number of Aux buffers in the framebuffer.
171             PFD_MAIN_PLANE,
172             0,
173             0, 0, 0
174         };
175         int formatIndex = ChoosePixelFormat(hdc, &pfd);
176         if(formatIndex == 0)
177         {
178             MessageBox(NULL, "Could not choose pixel format!", "Error!", MB_ICONERROR | MB_OK);
179             return false;
180         }
181         if(!SetPixelFormat(hdc, formatIndex, &pfd))
182         {
183             MessageBox(NULL, "Could not set pixel format!", "Error!", MB_ICONERROR | MB_OK);
184             return false;
185         }
186         glContext = wglCreateContext(hdc);
187         if(glContext is null)
188         {
189             MessageBox(NULL, "Could not create OpenGL Context", "Error!", MB_ICONERROR | MB_OK);
190             return false;
191         }
192         if(!wglMakeCurrent(hdc, glContext))
193         {
194             MessageBox(NULL, "Coult not set OpenGL Context", "Error!", MB_ICONERROR | MB_OK);
195             return false;
196         }
197         if(majorVersion < 3 && minorVersion < 3) //This is not actually tested
198         {
199             if(!GetPixelFormat(hdc))
200             {
201                 MessageBox(NULL, "Could not get window pixel format!", "Error!", MB_ICONEXCLAMATION | MB_OK);
202                 return false;
203             }
204             if(!DescribePixelFormat(hdc, formatIndex, pfd.sizeof, &pfd))
205             {
206                 MessageBox(NULL, "Could not get describe pixel format!", "Error!", MB_ICONEXCLAMATION | MB_OK);
207                 return false;
208             }
209             if((pfd.dwFlags & PFD_SUPPORT_OPENGL) != PFD_SUPPORT_OPENGL)
210             {
211                 MessageBox(NULL, "PixelFormatDescriptor does not support opengl!", "Error!", MB_ICONEXCLAMATION | MB_OK);
212                 return false;
213             }
214             return true;
215         }
216         else
217             return initializeModernOpenGL(hwnd, majorVersion, minorVersion);
218     }
219 
220     package bool initializeModernOpenGL(ref HWND hwnd, int majorVersion, int minorVersion) nothrow @nogc
221     {
222         //Load Function Pointers
223         wglChoosePixelFormatARB = cast(wglChoosePixelFormatARBProc)wglGetProcAddress("wglChoosePixelFormatARB");
224         if(wglChoosePixelFormatARB is null)
225         {
226             MessageBox(NULL, "Could not load wglChoosePixelFormatARB", "Error", MB_ICONERROR | MB_OK);
227             return false;
228         }
229         wglCreateContextAttribsARB = cast(wglCreateContextAttribsARBProc)wglGetProcAddress("wglCreateContextAttribsARB");
230         if(wglCreateContextAttribsARB is null)
231         {
232             MessageBox(NULL, "Could not load wglCreateContextAttribsARB", "Error", MB_ICONERROR | MB_OK);
233             return false;
234         }
235         wglSwapIntervalEXT = cast(wglSwapIntervalEXTProc)wglGetProcAddress("wglSwapIntervalEXT");
236         if(wglSwapIntervalEXT is null)
237         {
238             MessageBox(NULL, "Could not load wglSwapIntervalEXT", "Error", MB_ICONERROR | MB_OK);
239             return false;
240         }
241         //Now, for the modern OpenGL
242         const int[19] attribList =
243         [
244             WGL_DRAW_TO_WINDOW_ARB, true,
245             WGL_SUPPORT_OPENGL_ARB, true,
246             WGL_DOUBLE_BUFFER_ARB, true,
247             WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
248             WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
249             WGL_COLOR_BITS_ARB, 32,
250             WGL_DEPTH_BITS_ARB, 24,
251             WGL_STENCIL_BITS_ARB, 8,
252             WGL_ALPHA_BITS_ARB, 8,
253             0, // End
254         ];
255 
256         int pixelFormat;
257         uint numFormats;
258 
259         if(!wglChoosePixelFormatARB(hdc, attribList.ptr, null, 1, &pixelFormat, &numFormats) || numFormats == 0)
260         {
261             MessageBox(NULL, "Could notchoose pixel format", "Error", MB_ICONERROR | MB_OK);
262             return false;
263         }
264 
265         auto oldHwnd = hwnd;
266         HDC oldHDC = hdc;
267         HGLRC oldGLContext = glContext;
268 
269         RECT rBorders;
270         GetWindowRect(hwnd, &rBorders);
271         RECT rNoBorders;
272         GetClientRect(hwnd, &rNoBorders);
273         RECT r;
274         r.left = rBorders.left*2 - rNoBorders.left;
275         r.right = rBorders.right*2 - rNoBorders.right;
276         r.top = rBorders.top*2 - rNoBorders.top;
277         r.bottom = rBorders.bottom*2 - rNoBorders.bottom;
278         //Create 
279         hwnd = createWindow(r.right - r.left, r.bottom - r.top);
280 
281         hdc = GetDC(hwnd);
282 
283         PIXELFORMATDESCRIPTOR newPFD;
284         DescribePixelFormat(hdc, pixelFormat, newPFD.sizeof, &newPFD);
285         SetPixelFormat(hdc, pixelFormat, &newPFD);
286 
287         int[7] contextAttribs = 
288         [
289             WGL_CONTEXT_MAJOR_VERSION_ARB, majorVersion,
290             WGL_CONTEXT_MINOR_VERSION_ARB, minorVersion,
291             WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
292             0
293         ];
294         glContext = wglCreateContextAttribsARB(hdc, null, contextAttribs.ptr);
295         if(glContext is null)
296         {
297             MessageBox(null, "Could not create Modern OpenGL Context", "Error!", MB_ICONERROR | MB_OK);
298             return false;
299         }
300         wglMakeCurrent(null, null);
301         wglDeleteContext(oldGLContext);
302         ReleaseDC(oldHwnd, oldHDC);
303         DestroyWindow(oldHwnd);
304         if(!wglMakeCurrent(hdc, glContext))
305         {
306             MessageBox(null, "Could not set Modern OpenGL Context", "Error!", MB_ICONERROR | MB_OK);
307             return false;
308         }
309         return true;
310 
311     }
312 
313     bool registerClass()
314     {
315         HINSTANCE hInstance = GetModuleHandle(null);
316         WNDCLASS wc;
317         //Register window class
318 
319         wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
320         wc.lpfnWndProc = &WndProc;
321         wc.cbClsExtra = 0;
322         wc.cbWndExtra = 0;
323         wc.hInstance = hInstance; //Application handle
324         wc.hIcon = LoadIcon(null, IDI_APPLICATION); //Big icon
325         wc.hCursor = LoadCursor(null, IDC_ARROW); //Cursor
326         wc.hbrBackground = cast(HBRUSH)(COLOR_WINDOW); //Background brush
327         wc.lpszMenuName = null; //Name of menu resource
328         wc.lpszClassName = winClassName; //Name to identify this class of windows
329 
330         if(!RegisterClass(cast(const(WNDCLASSW)*)&wc))
331         {
332             uint err = GetLastError();
333             wchar* buffer;
334             uint size = FormatMessage(
335                 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 
336                 cast(void*)null,err, 0, cast(LPWSTR)&buffer, 0, null);
337             MessageBox(NULL, buffer, "Window Registration Failed with message: ", MB_ICONEXCLAMATION | MB_OK);
338             LocalFree(buffer);
339             return false;
340         }
341         return true;
342     }
343     package HWND createWindow(int width, int height) @nogc nothrow
344     {
345         return CreateWindowEx(
346             0,
347             winClassName,
348             winClassName, //Title
349             WS_OVERLAPPEDWINDOW | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE,
350             CW_USEDEFAULT, CW_USEDEFAULT,
351             width, height, HWND_DESKTOP, null, GetModuleHandle(null), null
352         );
353     }
354 
355     extern(Windows) LRESULT openWindow(ref int width, ref int height, out HWND hwnd)
356     {
357         static bool registeredClass = false;
358         if(!registeredClass)
359         {
360             if(!registerClass())
361                 return 0;
362         }
363         hwnd = createWindow(width, height);
364         if(hwnd == null)
365         {
366             MessageBox(NULL, "Window Creation Failed!", "Error!", MB_ICONEXCLAMATION | MB_OK);
367             return 0;
368         }
369         hdc = GetDC(hwnd);
370         
371         return 1;
372     }
373 
374     void show(HWND hwnd)
375     {
376         ShowWindow(hwnd, SW_NORMAL);
377         UpdateWindow(hwnd);
378     }
379 
380     void poll()
381     {
382         MSG msg;
383         while(PeekMessage(&msg, cast(void*)null, 0,0, PM_REMOVE)) //GetMessage may be a lot better 
384         {
385             TranslateMessage(&msg);
386             DispatchMessage(&msg);
387         }
388     }
389 
390     void swapBuffer()
391     {
392         SwapBuffers(hdc);
393     }
394 
395     int[2] getWindowSize(HWND hwnd, ref string[] errors)
396     {
397         RECT rect;
398         GetClientRect(hwnd, &rect);
399         return [rect.right - rect.left, rect.bottom - rect.top];
400     }
401     void setWindowName(string name, HWND hwnd, ref string[] errors)
402     {
403         SetWindowTextA(hwnd, name.ptr);
404     }
405     int[2] getWindowBorder(HWND hwnd)
406     {
407         RECT rBorders;
408         GetWindowRect(hwnd, &rBorders);
409         RECT rNoBorders;
410         GetClientRect(hwnd, &rNoBorders);
411         RECT r;
412         r.left = rBorders.left - rNoBorders.left;
413         r.right = rBorders.right - rNoBorders.right;
414         r.top = rBorders.top - rNoBorders.top;
415         r.bottom = rBorders.bottom - rNoBorders.bottom;
416 
417         return[r.right - r.left, r.bottom - r.top];
418     }
419 
420     void setWindowSize(int width, int height, HWND hwnd, ref string[] errors)
421     {
422         int[2] borders = getWindowBorder(hwnd);
423         SetWindowPos(hwnd, null, 0, 0, width + borders[0], height+borders[1], SWP_NOMOVE);
424     }
425 
426     void setVsyncActive(bool active, void* WindowHandle, ref string[] errors) @nogc nothrow @system
427     {
428         if(wglSwapIntervalEXT !is null)
429         {
430             wglSwapIntervalEXT(cast(int)active);
431         }
432     }
433 
434     extern(Windows) bool destroy_GL_Context()
435     {
436         if(!wglMakeCurrent(hdc, null))
437         {
438             MessageBox(NULL, "Could not detach OpenGL Context!", "Error!", MB_ICONEXCLAMATION | MB_OK);
439             return false;
440         }
441         if(!wglDeleteContext(glContext))
442         {
443             MessageBox(NULL, "Could not delete OpenGL Context!", "Error!", MB_ICONEXCLAMATION | MB_OK);
444             return false;
445         }
446         glContext = null;
447         return true;
448     }
449 
450     import hip.windowing.platforms.null_;
451     alias setFullscreen =  hip.windowing.platforms.null_.setFullscreen;
452 }
453 
454 
455 
456 enum WGL_ARB_pixel_format= 1;
457 enum WGL_NUMBER_PIXEL_FORMATS_ARB     = 0x2000;
458 enum WGL_DRAW_TO_WINDOW_ARB           = 0x2001;
459 enum WGL_DRAW_TO_BITMAP_ARB           = 0x2002;
460 enum WGL_ACCELERATION_ARB             = 0x2003;
461 enum WGL_NEED_PALETTE_ARB             = 0x2004;
462 enum WGL_NEED_SYSTEM_PALETTE_ARB      = 0x2005;
463 enum WGL_SWAP_LAYER_BUFFERS_ARB       = 0x2006;
464 enum WGL_SWAP_METHOD_ARB              = 0x2007;
465 enum WGL_NUMBER_OVERLAYS_ARB          = 0x2008;
466 enum WGL_NUMBER_UNDERLAYS_ARB         = 0x2009;
467 enum WGL_TRANSPARENT_ARB              = 0x200A;
468 enum WGL_TRANSPARENT_RED_VALUE_ARB    = 0x2037;
469 enum WGL_TRANSPARENT_GREEN_VALUE_ARB  = 0x2038;
470 enum WGL_TRANSPARENT_BLUE_VALUE_ARB   = 0x2039;
471 enum WGL_TRANSPARENT_ALPHA_VALUE_ARB  = 0x203A;
472 enum WGL_TRANSPARENT_INDEX_VALUE_ARB  = 0x203B;
473 enum WGL_SHARE_DEPTH_ARB              = 0x200C;
474 enum WGL_SHARE_STENCIL_ARB            = 0x200D;
475 enum WGL_SHARE_ACCUM_ARB              = 0x200E;
476 enum WGL_SUPPORT_GDI_ARB              = 0x200F;
477 enum WGL_SUPPORT_OPENGL_ARB           = 0x2010;
478 enum WGL_DOUBLE_BUFFER_ARB            = 0x2011;
479 enum WGL_STEREO_ARB                   = 0x2012;
480 enum WGL_PIXEL_TYPE_ARB               = 0x2013;
481 enum WGL_COLOR_BITS_ARB               = 0x2014;
482 enum WGL_RED_BITS_ARB                 = 0x2015;
483 enum WGL_RED_SHIFT_ARB                = 0x2016;
484 enum WGL_GREEN_BITS_ARB               = 0x2017;
485 enum WGL_GREEN_SHIFT_ARB              = 0x2018;
486 enum WGL_BLUE_BITS_ARB                = 0x2019;
487 enum WGL_BLUE_SHIFT_ARB               = 0x201A;
488 enum WGL_ALPHA_BITS_ARB               = 0x201B;
489 enum WGL_ALPHA_SHIFT_ARB              = 0x201C;
490 enum WGL_ACCUM_BITS_ARB               = 0x201D;
491 enum WGL_ACCUM_RED_BITS_ARB           = 0x201E;
492 enum WGL_ACCUM_GREEN_BITS_ARB         = 0x201F;
493 enum WGL_ACCUM_BLUE_BITS_ARB          = 0x2020;
494 enum WGL_ACCUM_ALPHA_BITS_ARB         = 0x2021;
495 enum WGL_DEPTH_BITS_ARB               = 0x2022;
496 enum WGL_STENCIL_BITS_ARB             = 0x2023;
497 enum WGL_AUX_BUFFERS_ARB              = 0x2024;
498 enum WGL_NO_ACCELERATION_ARB          = 0x2025;
499 enum WGL_GENERIC_ACCELERATION_ARB     = 0x2026;
500 enum WGL_FULL_ACCELERATION_ARB        = 0x2027;
501 enum WGL_SWAP_EXCHANGE_ARB            = 0x2028;
502 enum WGL_SWAP_COPY_ARB                = 0x2029;
503 enum WGL_SWAP_UNDEFINED_ARB           = 0x202A;
504 enum WGL_TYPE_RGBA_ARB                = 0x202B;
505 enum WGL_TYPE_COLORINDEX_ARB          = 0x202C;
506 
507 
508 enum WGL_CONTEXT_MAJOR_VERSION_ARB           = 0x2091;
509 enum WGL_CONTEXT_MINOR_VERSION_ARB           = 0x2092;
510 enum WGL_CONTEXT_LAYER_PLANE_ARB             = 0x2093;
511 enum WGL_CONTEXT_FLAGS_ARB                   = 0x2094;
512 enum WGL_CONTEXT_PROFILE_MASK_ARB            = 0x9126;
513 
514 
515 enum WGL_CONTEXT_DEBUG_BIT_ARB               = 0x0001;
516 enum WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB  = 0x0002;
517 
518 enum WGL_CONTEXT_CORE_PROFILE_BIT_ARB         = 0x00000001;
519 enum WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB  = 0x00000002;
520 
521 
522 enum ERROR_INVALID_VERSION_ARB               = 0x2095;
523 enum ERROR_INVALID_PROFILE_ARB               = 0x2096;